#pragma once

#include <vsg/all.h>

#include "./materials.hpp"
#include "./meshs.hpp"

namespace qbe::render {

class LineMeshNode : public vsg::Inherit<vsg::StateGroup, LineMeshNode> {

 public:
  vsg::ref_ptr<vsg::Options> options;
  Mesh2D mesh;
  LineShaderMaterial2D material;
  qlib::Tag tag;
  VkPrimitiveTopology primitiveTopology{VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP};
  vsg::ref_ptr<vsg::Value<LineUniform>> uniform;
  bool instance{false};

  std::string name;

 public:
  LineMeshNode();
  ~LineMeshNode();
  LineMeshNode(vsg::ref_ptr<vsg::Options> _options, Mesh2D _mesh, LineShaderMaterial2D _material, VkPrimitiveTopology _primitiveTopology);

  template <typename... Args>
  static vsg::ref_ptr<LineMeshNode> create(Args&&... args) {
    return vsg::ref_ptr<LineMeshNode>(new LineMeshNode(std::forward<Args>(args)...));
  }

  void addPipe();
  void clear();
  void addCommands();

  void addMesh(const Mesh2D& mh);
  void addMeshs(const std::vector<Mesh2D>& meshs);
  void update();
  void updateUniform();
};

class InstanceLineMeshNode : public vsg::Inherit<LineMeshNode, InstanceLineMeshNode> {
 public:
  InstanceLineMeshNode() {};
  ~InstanceLineMeshNode() {};

  InstanceLineMeshNode(vsg::ref_ptr<vsg::Options> _options, Mesh2D _mesh, LineShaderMaterial2D _material, VkPrimitiveTopology _primitiveTopology);

  void addInstanceCmd(std::vector<Mesh2D>& meshs, uint32_t instance_count, vsg::ref_ptr<vsg::vec3Array> instance_v1,
                      vsg::ref_ptr<vsg::vec3Array> instance_v2, vsg::ref_ptr<vsg::vec3Array> instance_v3);

  void addInstance(std::vector<Mesh2D>& meshs, InstanceMesh2D& instance_mesh);
  void updateInstance(std::vector<Mesh2D>& meshs, InstanceMesh2D& instance_mesh);

  void updateInstance(std::vector<Mesh2D>& meshs, uint32_t instance_count, vsg::ref_ptr<vsg::vec3Array> instance_v1,
                      vsg::ref_ptr<vsg::vec3Array> instance_v2, vsg::ref_ptr<vsg::vec3Array> instance_v3);
};

class FillMeshNode : public vsg::Inherit<vsg::StateGroup, FillMeshNode> {

 public:
  vsg::ref_ptr<vsg::Options> options;
  Mesh2D mesh;
  FillShaderMaterial2D material;
  qlib::Tag tag;
  VkPrimitiveTopology primitiveTopology{VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP};
  vsg::ref_ptr<vsg::Value<FillUniform>> uniform;

  std::string name;

 public:
  FillMeshNode();
  ~FillMeshNode();
  FillMeshNode(vsg::ref_ptr<vsg::Options> _options, Mesh2D _mesh, FillShaderMaterial2D _material, VkPrimitiveTopology _primitiveTopology);

  template <typename... Args>
  static vsg::ref_ptr<FillMeshNode> create(Args&&... args) {
    return vsg::ref_ptr<FillMeshNode>(new FillMeshNode(std::forward<Args>(args)...));
  }

  void updateUniform();
  void addPipe();

  void clear();
  void addMesh(const Mesh2D& mh);
  void addMeshs(const std::vector<Mesh2D>& meshs);
};

// class MeshNodeNew : public vsg::Inherit<vsg::StateGroup, MeshNodeNew> {
//  public:
//   MeshNodeNew() {};
//   ~MeshNodeNew() {};
//   MeshNodeNew(vsg::ref_ptr<vsg::Options> _options, Mesh2D& _mesh, BorderMaterial& _material, VkPrimitiveTopology _primitiveTopology);
//   // MeshNode(vsg::ref_ptr<vsg::Options> _options, Mesh2D _mesh, FillMaterial _material, VkPrimitiveTopology _primitiveTopology)
//   //     : options(_options), mesh(_mesh), material(_material), primitiveTopology(_primitiveTopology) {}

//   template <typename... Args>
//   static vsg::ref_ptr<MeshNodeNew> create(Args&&... args) {
//     return vsg::ref_ptr<MeshNodeNew>(new MeshNodeNew(std::forward<Args>(args)...));
//   }

//   template <class UTy>
//   void addPipe(vsg::ref_ptr<vsg::Options> options, vsg::Path vert_path, vsg::Path frag_path, UTy uniform, VkPrimitiveTopology primitiveTopology,
//                bool instance) {
//     auto vertexShader = vsg::ShaderStage::read(VK_SHADER_STAGE_VERTEX_BIT, "main", vsg::findFile(vert_path, options));
//     auto fragmentShader = vsg::ShaderStage::read(VK_SHADER_STAGE_FRAGMENT_BIT, "main", vsg::findFile(frag_path, options));
//     if (!vertexShader || !fragmentShader) {
//       SPDLOG_INFO("Could not create shaders.");
//       // return;
//     }

//     // Uniform绑定
//     vsg::DescriptorSetLayoutBindings descriptorBindings{
//         {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT, nullptr},  // ClipSettings uniform
//         // { binding, descriptorType, descriptorCount, stageFlags, pImmutableSamplers}
//     };

//     auto descriptorSetLayout = vsg::DescriptorSetLayout::create(descriptorBindings);

//     // projection, view, and model matrices, actual push constant calls automatically provided by the VSG's RecordTraversal
//     vsg::PushConstantRanges pushConstantRanges{{VK_SHADER_STAGE_VERTEX_BIT, 0, 128}};

//     vsg::VertexInputState::Bindings vertexBindingsDescriptions = vsg::VertexInputState::Bindings{
//         VkVertexInputBindingDescription{0, sizeof(vsg::vec2), VK_VERTEX_INPUT_RATE_VERTEX},  // vertex data
//     };
//     vsg::VertexInputState::Attributes vertexAttributeDescriptions = vsg::VertexInputState::Attributes{
//         VkVertexInputAttributeDescription{0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0},  // vertex data
//     };

//     if (instance) {
//       vertexBindingsDescriptions = vsg::VertexInputState::Bindings{
//           VkVertexInputBindingDescription{0, sizeof(vsg::vec2), VK_VERTEX_INPUT_RATE_VERTEX},    // vertex data
//           VkVertexInputBindingDescription{1, sizeof(vsg::vec3), VK_VERTEX_INPUT_RATE_INSTANCE},  // instance tran
//           VkVertexInputBindingDescription{2, sizeof(vsg::vec3), VK_VERTEX_INPUT_RATE_INSTANCE},  // instance mxr
//           VkVertexInputBindingDescription{3, sizeof(vsg::vec3), VK_VERTEX_INPUT_RATE_INSTANCE},  // instance mxr
//       };
//       vertexAttributeDescriptions = vsg::VertexInputState::Attributes{
//           VkVertexInputAttributeDescription{0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0},  // vertex data
//           VkVertexInputAttributeDescription{1, 1, VK_FORMAT_R32G32B32_SFLOAT, 0},  // instance tran
//           VkVertexInputAttributeDescription{2, 2, VK_FORMAT_R32G32B32_SFLOAT, 0},  // instance mxr
//           VkVertexInputAttributeDescription{3, 3, VK_FORMAT_R32G32B32_SFLOAT, 0},  // instance mxr
//       };
//     }

//     // 图元重启默认开启
//     VkBool32 primitiveRestart = VK_TRUE;

//     vsg::GraphicsPipelineStates pipelineStates{vsg::VertexInputState::create(vertexBindingsDescriptions, vertexAttributeDescriptions),
//                                                vsg::InputAssemblyState::create(primitiveTopology, primitiveRestart),
//                                                vsg::RasterizationState::create(),
//                                                vsg::MultisampleState::create(),
//                                                vsg::ColorBlendState::create(),
//                                                vsg::DepthStencilState::create()};

//     auto pipelineLayout = vsg::PipelineLayout::create(vsg::DescriptorSetLayouts{descriptorSetLayout}, pushConstantRanges);
//     auto graphicsPipeline = vsg::GraphicsPipeline::create(pipelineLayout, vsg::ShaderStages{vertexShader, fragmentShader}, pipelineStates);
//     auto bindGraphicsPipeline = vsg::BindGraphicsPipeline::create(graphicsPipeline);

//     // create UniformValue and associated DescriptorSets and binding
//     auto uniformBuffer = vsg::DescriptorBuffer::create(uniform, 0);
//     auto descriptorSet = vsg::DescriptorSet::create(descriptorSetLayout, vsg::Descriptors{uniformBuffer});
//     auto bindDescriptorSet = vsg::BindDescriptorSet::create(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, descriptorSet);

//     // create StateGroup as the root of the scene/command graph to hold the GraphicsPipeline, and binding of Descriptors to decorate the whole graph
//     this->add(bindGraphicsPipeline);
//     this->add(bindDescriptorSet);
//   }

//   void clear();
//   void addMesh(const Mesh2D& mh);
//   void addMeshs(const std::vector<Mesh2D>& meshs);
//   void updateMesh(const Mesh2D& mh);
//   void updateMeshs(const std::vector<Mesh2D>& meshs);

//  private:
//   vsg::ref_ptr<vsg::Options> options;
//   Mesh2D mesh;
//   Material material;
//   VkPrimitiveTopology primitiveTopology{VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP};
//   bool instance{false};
// };

class MeshNode : public vsg::Inherit<vsg::StateGroup, MeshNode> {

 public:
  vsg::ref_ptr<vsg::Options> options;

  Mesh2D mesh;
  ShaderMaterial2D material;

  VkPrimitiveTopology primitiveTopology{VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP};
  bool instance{false};

  std::string name;

 public:
  MeshNode() {};
  ~MeshNode() {};

  MeshNode(vsg::ref_ptr<vsg::Options> _options, Mesh2D _mesh, ShaderMaterial2D _material,
           VkPrimitiveTopology _primitiveTopology = VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, bool _instance = false);

  template <typename... Args>
  static vsg::ref_ptr<MeshNode> create(Args&&... args) {
    return vsg::ref_ptr<MeshNode>(new MeshNode(std::forward<Args>(args)...));
  }

  void addPipe();

  void clear();

  void addCommands();

  void addMesh(const Mesh2D& mh);

  void update();

  void addCmd(const std::vector<Mesh2D>& meshs);

  void addMeshs(const std::vector<Mesh2D>& meshs);

  void updateMesh(const Mesh2D& mh);

  void updateMeshs(const std::vector<Mesh2D>& meshs);
};

class InstanceMeshNode : public vsg::Inherit<MeshNode, InstanceMeshNode> {
 public:
  InstanceMeshNode() {};
  ~InstanceMeshNode() {};

  InstanceMeshNode(vsg::ref_ptr<vsg::Options> _options, Mesh2D _mesh, ShaderMaterial2D _material,
                   VkPrimitiveTopology _primitiveTopology = VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP);

  void addInstanceCmd(std::vector<Mesh2D>& meshs, uint32_t instance_count, vsg::ref_ptr<vsg::vec3Array> instance_v1,
                      vsg::ref_ptr<vsg::vec3Array> instance_v2, vsg::ref_ptr<vsg::vec3Array> instance_v3);

  void addInstance(std::vector<Mesh2D>& meshs, InstanceMesh2D& instance_mesh);
  void updateInstance(std::vector<Mesh2D>& meshs, InstanceMesh2D& instance_mesh);

  void updateInstance(std::vector<Mesh2D>& meshs, uint32_t instance_count, vsg::ref_ptr<vsg::vec3Array> instance_v1,
                      vsg::ref_ptr<vsg::vec3Array> instance_v2, vsg::ref_ptr<vsg::vec3Array> instance_v3);
};

}  // namespace qbe::render